/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package com.guchenbo.example.netty;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.security.auth.callback.*;
import javax.security.sasl.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * Handles a server-side channel.
 */
@Sharable
public class SaslServerHandler extends SimpleChannelInboundHandler<SaslMessage> {
    protected static final Logger log = LoggerFactory.getLogger(SaslServerHandler.class);
    private SaslServer saslServer;

    public SaslServerHandler() {
        String mechanism = "DIGEST-MD5"; // 选择SASL机制
        String protocol = "example"; // 使用的协议
        String serverName = "myServer"; // 服务器名
        Map<String, String> opts = new HashMap<>();
        // 客户端提供的用户名和密码
        String username = "myUser";
        String password = "myPassword";

        try {
            this.saslServer = Sasl.createSaslServer(mechanism, protocol, serverName, opts, new CallbackHandler() {
                @Override
                public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
                    for (Callback callback : callbacks) {
                        if (callback instanceof AuthorizeCallback) {
                            AuthorizeCallback ac = (AuthorizeCallback) callback;
                            //Perform application-specific authorization action
                            ac.setAuthorized(false);
                        }else if (callback instanceof NameCallback) {
                            // 处理用户名
                            NameCallback nameCallback = (NameCallback) callback;
                            nameCallback.setName(username);
                        } else if (callback instanceof PasswordCallback) {
                            // 处理密码
                            PasswordCallback passwordCallback = (PasswordCallback) callback;
                            passwordCallback.setPassword(password.toCharArray());
                        } else if (callback instanceof RealmCallback) {
                            RealmCallback rc = (RealmCallback) callback;
                            //Collect realm data in application-specific manner
                            rc.setText("myServer");
                        } else {
                            log.warn("UnsupportedCallbackException {}", callback);
                            throw new UnsupportedCallbackException(callback, "不支持的回调类型");
                        }
                    }
                }
            });
        } catch (SaslException e) {
            throw new RuntimeException(e);
        }
        ;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // Send greeting for a new connection.
//        log.info("channel active {}", ctx.channel());
//        ctx.write("Welcome to " + InetAddress.getLocalHost().getHostName() + "!\r\n");
//        ctx.write("It is " + new Date() + " now.\r\n");
//        ctx.flush();
        log.info("channelActive {}",saslServer.isComplete());
        byte[] challenge = saslServer.evaluateResponse("".getBytes(StandardCharsets.UTF_8));
        SaslMessage response = new SaslMessage();
        response.setMsg("server");
        response.setChallenge(challenge);
        ChannelFuture future = ctx.writeAndFlush(response);

    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, SaslMessage request) throws Exception {
        // Generate and write a response.
        log.info("request {},{}", request.getMsg(),saslServer.isComplete());
        byte[] challenge = saslServer.evaluateResponse(request.getChallenge());
        if (challenge != null) {
            SaslMessage response = new SaslMessage();
            response.setMsg("server");
            response.setChallenge(challenge);

            // We do not need to write a ChannelBuffer here.
            // We know the encoder inserted at TelnetPipelineFactory will do the conversion.
            ChannelFuture future = ctx.writeAndFlush(response);
            future.sync();
        }

        System.out.println(saslServer.isComplete());

    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("channel leave {}", ctx.channel());
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}
